package com.abel.bigwater.kmgw

import com.abel.bigwater.data.BcdString
import com.abel.bigwater.dataserver.model.PojoHelper
import com.abel.bigwater.dataserver.model.kming.*
import com.abel.bigwater.kmgw.dao.FlowDao
import io.netty.channel.ChannelHandlerContext
import io.netty.channel.ChannelInboundHandlerAdapter
import org.slf4j.LoggerFactory
import java.util.*

class KmingServerHandler(val kmingDao: FlowDao, val colName: String) : ChannelInboundHandlerAdapter() {
    override fun channelActive(ctx: ChannelHandlerContext?) {
        lgr.info("Got connection from ${ctx?.channel()?.remoteAddress()}")
    }

    override fun channelRead(ctx: ChannelHandlerContext?, msg: Any?) {
        lgr.info("$msg from ${ctx?.channel()?.remoteAddress()}")

        when {
            msg is KmingPacket -> {
                val fd = PojoHelper.toPojo(msg)
                kmingDao.upsert(fd!!, colName)

                // reply back
                when (msg.data) {
                    is TermUploadReqData -> {
                        replyUploadReq(msg, ctx)
                    }
                    is TermSendData -> {
                        replySendData(msg, ctx)
                    }

                    is TermHeartData -> {
                        replyHeartData(msg, ctx)
                    }

                    is TermShutdownData -> {
                        replyShutdownData(msg, ctx)
                    }
                }
            }
            else -> lgr.warn("other packet type: ${msg?.javaClass}")
        }
    }

    private fun replyShutdownData(msg: KmingPacket, ctx: ChannelHandlerContext?): KmingPacket {
        val rply = KmingPacket().apply {
            termCode = msg.termCode
            deviceStatus = msg.deviceStatus
            limitStatus = msg.limitStatus
            sessionStatus = SessionStatus().also {
                it.serialNo = msg.sessionStatus.serialNo
            }

            data = KmingRepondShutdown().also {
                it.startTime = Date()
                it.sessionReponseType = SessionReponseType.Success

                dataLen = it.bufLength()
            }
        }

        val buf = ctx!!.alloc().buffer().also { rply.pack(it) }
        lgr.info("reply to device for shutdown: ${BcdString.hexString(buf, withBlank = true)}")
        ctx.writeAndFlush(buf)

        return rply
    }

    private fun replyHeartData(msg: KmingPacket, ctx: ChannelHandlerContext?): KmingPacket {
        val rply = KmingPacket().apply {
            termCode = msg.termCode
            deviceStatus = msg.deviceStatus
            limitStatus = msg.limitStatus
            sessionStatus = SessionStatus().also {
                it.serialNo = msg.sessionStatus.serialNo
            }

            data = KmingHeartData().also {
                it.startTime = Date()

                dataLen = it.bufLength()
            }
        }

        val buf = ctx!!.alloc().buffer().also { rply.pack(it) }
        lgr.info("reply to device for heart: ${BcdString.hexString(buf, withBlank = true)}")
        ctx.writeAndFlush(buf)

        return rply
    }

    /**
     * reply to send data.
     */
    private fun replySendData(msg: KmingPacket, ctx: ChannelHandlerContext?): KmingPacket {
        val rply = KmingPacket().apply {
            termCode = msg.termCode
            deviceStatus = msg.deviceStatus
            limitStatus = msg.limitStatus
            sessionStatus = SessionStatus().also {
                it.serialNo = msg.sessionStatus.serialNo
            }

            data = KmingResponseData(KmingDataType.TermUploadReq).also {
                it.sessionReponseType = SessionReponseType.Success

                dataLen = it.bufLength()
            }
        }

        val buf = ctx!!.alloc().buffer().also { rply.pack(it) }
        lgr.info("reply to device for data: ${BcdString.hexString(buf, withBlank = true)}")
        ctx.writeAndFlush(buf)

        return rply
    }

    /**
     * reply to term-upload-req.
     */
    private fun replyUploadReq(msg: KmingPacket, ctx: ChannelHandlerContext?): KmingPacket {
        val rply = KmingPacket().apply {
            termCode = msg.termCode
            deviceStatus = msg.deviceStatus
            limitStatus = msg.limitStatus
            sessionStatus = SessionStatus().also {
                it.serialNo = msg.sessionStatus.serialNo
            }

            data = KmingResponseData(KmingDataType.TermUploadReq).also {
                it.sessionReponseType = SessionReponseType.Success

                dataLen = it.bufLength()
            }
        }

        val buf = ctx!!.alloc().buffer().also { rply.pack(it) }
        lgr.info("reply to device for req: ${BcdString.hexString(buf, withBlank = true)}")
        ctx.writeAndFlush(buf)

        return rply
    }

    override fun exceptionCaught(ctx: ChannelHandlerContext?, cause: Throwable?) {
        lgr.error("try to reset connection caused by ${cause?.message} from ${ctx?.channel()?.remoteAddress()}", cause)
        ctx?.close()
    }

    companion object {
        private val lgr = LoggerFactory.getLogger(KmingServerHandler::class.java)
    }

}
